/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2020 Ioannis Moutsatsos, Bruno P. Kinoshita
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.biouno.unochoice;

import com.alibaba.fastjson.JSONObject;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import hudson.EnvVars;
import hudson.Extension;

import hudson.model.*;
import hudson.plugins.git.GitException;
import hudson.plugins.git.GitTool;
import hudson.util.ListBoxModel;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;

import org.biouno.unochoice.model.Script;
import org.eclipse.jgit.lib.ObjectId;
import org.jenkinsci.plugins.gitclient.Git;
import org.jenkinsci.plugins.gitclient.GitClient;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.QueryParameter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

import static org.biouno.unochoice.WorkspaceConfig.DescriptorImpl.lookupCredentials;

/**
 * A parameter that renders its options as a choice (select) HTML component.
 *
 * @author Bruno P. Kinoshita
 * @since 0.1
 */
public class ChoiceParameter extends AbstractScriptableParameter {

    /*
     * Serial UID.
     */
    private static final long serialVersionUID = -4449319038169585222L;

    /**
     * Choice type.
     */
    private final String choiceType;

    /**
     * Filter flag.
     */
    private final Boolean filterable;

    /**
     * Filter length. Defines a minimum number of characters that must be entered before the filter
     * is activated.
     */
    private final Integer filterLength;

    private final List<WorkspaceConfig> workspaceConfigs;


    /**
     * Constructor called from Jelly with parameters.
     *
     * @param name        name
     * @param description description
     * @param script      script
     * @param choiceType  choice type
     * @param filterable  filter flag
     * @deprecated see JENKINS-32149
     */
    @Deprecated
    public ChoiceParameter(String name, String description, Script script, String choiceType, Boolean filterable, List<WorkspaceConfig> workspaceConfigs) {
        super(name, description, script);
        this.filterable = filterable;
        this.workspaceConfigs = workspaceConfigs;
        this.filterLength = null;
        this.choiceType = StringUtils.defaultIfBlank(choiceType, PARAMETER_TYPE_SINGLE_SELECT);
        MySQLDatabaseUtils.updateProjectInfo(this.getProjectFullName(), JSONObject.toJSONString(getWorkspaceConfigsB(workspaceConfigs)));
    }

    public List<JSONObject> getWorkspaceConfigsB(List<WorkspaceConfig> workspaceConfigs) {
        List<JSONObject> list = new ArrayList<>();
        try {
            for (WorkspaceConfig userRemoteConfig : workspaceConfigs) {
                list.add(userRemoteConfig.toJSONObject());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }


    /**
     * Constructor called from Jelly with parameters.
     *
     * @param name        name
     * @param description description
     * @param randomName  parameter random generated name
     * @param script      script
     * @param choiceType  choice type
     * @param filterable  filter flag
     * @deprecated see JENKINS-31625
     */
    @Deprecated
    public ChoiceParameter(String name, String description, String randomName, Script script, String choiceType,
                           Boolean filterable, List<WorkspaceConfig> workspaceConfigs) {
        super(name, description, randomName, script);
        this.filterable = filterable;
        this.workspaceConfigs = workspaceConfigs;
        this.filterLength = null;
        this.choiceType = StringUtils.defaultIfBlank(choiceType, PARAMETER_TYPE_SINGLE_SELECT);
        MySQLDatabaseUtils.updateProjectInfo(this.getProjectFullName(), JSONObject.toJSONString(getWorkspaceConfigsB(workspaceConfigs)));
    }

    /**
     * Constructor called from Jelly with parameters.
     *
     * @param name         name
     * @param description  description
     * @param randomName   parameter random generated name
     * @param script       script
     * @param choiceType   choice type
     * @param filterable   filter flag
     * @param filterLength length when filter start filtering
     */
    @DataBoundConstructor
    public ChoiceParameter(String name, String description, String randomName, Script script, String choiceType,
                           Boolean filterable, Integer filterLength, List<WorkspaceConfig> workspaceConfigs) {
        super(name, description, randomName, script);
        this.filterable = filterable;
        this.filterLength = filterLength;
        this.workspaceConfigs = workspaceConfigs;
        this.choiceType = StringUtils.defaultIfBlank(choiceType, PARAMETER_TYPE_SINGLE_SELECT);
        MySQLDatabaseUtils.updateProjectInfo(this.getProjectFullName(), JSONObject.toJSONString(getWorkspaceConfigsB(workspaceConfigs)));
    }


    /*
     * (non-Javadoc)
     * @see org.biouno.unochoice.AbstractUnoChoiceParameter#getChoiceType()
     */
    @Override
    public String getChoiceType() {
        return this.choiceType;
    }

    /**
     * Get the filter flag.
     *
     * @return filter flag
     */
    public boolean getFilterable() {
        return Objects.equals(filterable, Boolean.TRUE);
    }

    /**
     * Get the filter length.
     *
     * @return filter length
     */
    public Integer getFilterLength() {
        return filterLength == null ? (Integer) 1 : filterLength;
    }

    public List<WorkspaceConfig> getWorkspaceConfigs() {
        return workspaceConfigs;
    }

    // --- descriptor

    @Extension
    @Symbol({"activeChoice"})
    public static final class DescriptorImpl extends UnoChoiceParameterDescriptor {


        @Override
        public String getDisplayName() {
            return "Active Choices Parameter";
        }

        @Override
        public String getHelpFile() {
            return "/plugin/uno-choice-database/help/parameter/active-choices-choice.html";

        }

        public ListBoxModel doFillBranchItems(@AncestorInPath Item item,
                                              @QueryParameter String url,
                                              @QueryParameter String credentialsId) {
            ListBoxModel r = new ListBoxModel();
            try {
                // Normally this permission is hidden and implied by Item.CONFIGURE, so from a view-only form you will not be able to use this check.
                // (TODO under certain circumstances being granted only USE_OWN might suffice, though this presumes a fix of JENKINS-31870.)
                if (item == null && !Jenkins.get().hasPermission(Jenkins.ADMINISTER) ||
                        item != null && !item.hasPermission(CredentialsProvider.USE_ITEM)) {
                    return r;
                }
                if (url == null || url.indexOf('$') >= 0)
                    // set by variable, can't validate
                    return r;

                // get git executable on controller
                EnvVars environment;
                Jenkins jenkins = Jenkins.get();
                if (item instanceof Job<?, ?> job) {
                    environment = job.getEnvironment(jenkins, TaskListener.NULL);
                } else {
                    Computer computer = jenkins.toComputer();
                    environment = computer == null ? new EnvVars() : computer.buildEnvironment(TaskListener.NULL);
                }

                GitClient git = Git.with(TaskListener.NULL, environment)
                        .using(GitTool.getDefaultInstallation().getGitExe())
                        .getClient();
                StandardCredentials credential = lookupCredentials(item, credentialsId, url);
                git.clearCredentials();
                git.addCredentials(url, credential);
                git.addDefaultCredentials(credential);
                try {
                    Map<String, ObjectId> branchs = git.getRemoteReferences(url, "refs/heads/*", false, false);
                    Map<String, ObjectId> release_tags = git.getRemoteReferences(url, "refs/tags/*", false, false);
                    AtomicReference<String> ci = new AtomicReference<>("");
                    AtomicReference<String> lastci = new AtomicReference<>("");
                    release_tags.keySet().forEach(branch -> {
                        String b = branch.replace("refs/tags/", "");
                        if (b.indexOf("ci_") == 0) {
                            ci.set(b);
                        } else if (b.indexOf("lastci_") == 0) {
                            lastci.set(b);
                        } else {
                            r.add(b + "<tag>", b);
                        }
                    });


                    branchs.keySet().forEach(branch -> {
                        String b = branch.replace("refs/heads/", "");
                        r.add(b + "<branch>", b);
                    });
                    if (lastci.get() != "") {
                        ListBoxModel.Option elementData = new ListBoxModel.Option(lastci.get() + "<tag>", lastci.get());
                        if (ci.get().equals("")) elementData.selected = true;
                        r.add(0, elementData);
                    }
                    if (ci.get() != "") {
                        ListBoxModel.Option elementData = new ListBoxModel.Option(ci.get() + "<tag>", ci.get());
                        elementData.selected = true;
                        r.add(0, elementData);
                    }
                } catch (GitException e) {
                    e.printStackTrace();
                    System.err.println("credentialsId:" + credentialsId);
                    return r;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return r;
        }


    }

}
